import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.bitfinex_v2.kr_rest.native_public_client as npubc
from coin.exchange.bitfinex_v2.kr_rest.constants import currency_list, quote_currency_list
from coin.exchange.bitfinex_v2.kr_rest.product import BitfinexProduct
from coin.exchange.bitfinex_v2.kr_rest.currency import to_native_currency

from coin.proto.coin_market_query_pb2 import ProductTicker, ExchangeTicker


class BitfinexFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg: list, product):
    keys = [
        'bid',
        'bid_size',
        'ask',
        'ask_size',
        'daily_change',
        'daily_change_perc',
        'last_price',
        'volume',
        'high',
        'low'
    ]
    update_msg = dict(zip(keys, update_msg))
    return ProductTicker(bid=float(update_msg['bid']),
                         bid_size=float(update_msg['bid_size']),
                         ask=float(update_msg['ask']),
                         ask_size=float(update_msg['ask_size']),
                         last=float(update_msg['last_price']),
                         volume_24hr=float(update_msg['volume']),
                         high=float(update_msg['high']),
                         low=float(update_msg['low']),
                         native_symbol=product.native_symbol,
                         symbol=product.symbol)

  @staticmethod
  def parse_exchange_ticker(update_msg: list):
    keys = [
        'symbol',
        'bid',
        'bid_size',
        'ask',
        'ask_size',
        'daily_change',
        'daily_change_perc',
        'last_price',
        'volume',
        'high',
        'low'
    ]

    product_ticker_list = []
    for raw_ticker in update_msg:
      try:
        ticker = dict(zip(keys, raw_ticker))
        product = BitfinexProduct.FromStrNativeProductNothrow(ticker['symbol'])
        if product is None:
          continue

        product_ticker = ProductTicker(bid=float(ticker['bid']),
                                       bid_size=float(ticker['bid_size']),
                                       ask=float(ticker['ask']),
                                       ask_size=float(ticker['ask_size']),
                                       last=float(ticker['last_price']),
                                       volume_24hr=float(ticker['volume']),
                                       high=float(ticker['high']),
                                       low=float(ticker['low']),
                                       native_symbol=product.native_symbol,
                                       symbol=product.symbol)
        product_ticker_list.append(product_ticker)
      except Exception as e:
        print('Fail to parse ticker', product, type(e), e)
        continue
    assert len(product_ticker_list) > 0
    return ExchangeTicker(exchange="Bitfinex", each_ticker=product_ticker_list)

  @staticmethod
  def parse_level_book(update_msg, product):
    level_book = level_book_mdl.LevelBook(product)
    for elem in update_msg:
      if elem[2] > 0:
        level_elem = level_book_mdl.LevelElement(elem[0], elem[2], elem[1])
        level_book.add_bid(level_elem)
      elif elem[2] < 0:
        level_elem = level_book_mdl.LevelElement(elem[0], -elem[2], elem[1])
        level_book.add_ask(level_elem)
      else:
        pass
    level_book.finalize()
    return level_book


class BitfinexPublicClient(pubcb.PublicClientBase):
  ProductType = BitfinexProduct

  def __init__(self):
    self.npubc = npubc.BitfinexNativePublicClient()

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_ticker(symbol)
    update.msg = BitfinexFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    all_symbols = []
    for currency in currency_list:
      for accounting_currency in quote_currency_list:
        if to_native_currency(currency) == to_native_currency(accounting_currency):
          continue
        all_symbols.append(BitfinexProduct.FromStrPair(currency, accounting_currency).native_symbol)
    symbols = ','.join(all_symbols)
    update = self.npubc.get_tickers(symbols)
    update.msg = BitfinexFeedParser.parse_exchange_ticker(update.msg)
    return update

  def query_level_book_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_books(symbol, "P0")
    update.msg = BitfinexFeedParser.parse_level_book(update.msg, product)
    return update
